//
//	(c) 2009 Andrew Kalmacky
//

#ifndef _AK_LTM_VECTOR_H_
#define _AK_LTM_VECTOR_H_

#include "ltm.h"
#include "../logger/logger.h"

namespace ak
{
	namespace ltm
	{
		template<size_t STEP>
		struct const_grow_stratey
		{
			size_t to_alloc(size_t current_size, size_t size_delta)
			{
				return current_size + size_delta + STEP;
			}
		};

		template< typename T, typename grow_strategy = const_grow_stratey<20> >
		class vector
		{
			template<typename A, typename B> friend void unsafe_swap(vector<A, B>&, vector<A, B>&);
		public:
			typedef T* iterator;
			typedef const T* const_iterator;

			explicit vector(size_t size = 0)
			{
				if (size)
				{
					_begin = _alloc(size);
					_end = _last_alloc = _begin + size;
					_construct(_begin, _end);
				}
				else
					_begin = _end = _last_alloc = NULL;
			}

			vector(const vector& src)
				: _grow_strategy(src._grow_strategy)
			{
				if (ltm::in_move)
				{
					_begin = src._begin;
					_end = src._end;
					_last_alloc = src._last_alloc;
				}
				else
				{
					_begin = _alloc(src.size());
					_end = _last_alloc = _begin + src.size();
					_construct_copy(_begin, src._begin, src._end);
				}
			}

			~vector()
			{
				_destruct(_begin, _end);
				_dispose_block(_begin);
			}

			vector<T, grow_strategy>& operator=(const vector<T, grow_strategy>& src)
			{
				if (this != &src)
				{
					this->~vector();
					new(this) vector<T, grow_strategy>(src);
				}
				return *this;
			}

			void dispose()
			{
				this->~vector();
				new(this) vector();
			}

			size_t size() const
			{
				return _end - _begin;
			}

			bool empty()
			{
				return _begin == _end;
			}

			void resize(size_t new_size)
			{
				int delta = int(new_size - size());
				if (delta == 0)
					return;
				if (delta < 0)
				{
					_destruct(_end + delta, _end);
					_end += delta;
					return;
				}
				if (_end + new_size >= _last_alloc)
					_realloc(new_size);
				_construct(_end, _end + delta);
				_end += delta;
			}

			void reserve(size_t to_reserve)
			{
				if (to_reserve > size_t(_last_alloc - _begin))
					_realloc(to_reserve);
			}

			const_iterator begin() const no_throw
			{
				return _begin;
			}
			const_iterator end() const no_throw
			{
				return _end;
			}
			iterator begin() no_throw
			{
				return _begin;
			}
			iterator end() no_throw
			{
				return _end;
			}

			T& back()
			{
				Log::memory::warning::assert(_end > _begin, T_("bounds in vector::back"));
				return _end[-1];
			}
			template<typename SUBST>
			T& push_back(const SUBST& p)
			{
				return *insert(end(), p);
			}
			T& push_back()
			{
				return *insert(end());
			}

			void pop_back()
			{
				Log::memory::warning::assert(_end > _begin, T_("pop_back at empty vector"));
				_destruct(--_end);
			}

			T& front()
			{
				Log::memory::warning::assert(_end > _begin, T_("bounds in vector::front"));
				return *_begin;
			}
			template<typename SUBST>
			T& push_front(const SUBST& p)
			{
				return *insert(begin(), p);
			}
			T& push_front()
			{
				return *insert(begin());
			}

			void pop_front()
			{
				erase(_begin);
			}

			iterator erase(iterator at)
			{
				Log::memory::warning::assert(at >= _begin && at < _end, T_("erase iterator out of vector"));
				_destruct(at);
				_move(at, at + 1, _end);
				--_end;
				return at;
			}

			iterator erase(size_t at)
			{
				return erase(begin() + at);
			}

			iterator erase(iterator from, iterator to)
			{
				Log::memory::warning::assert(from < to && from >= _begin && to <= _end, T_("erase iterators are out of vector"));
				_destruct(from, to);
				_move(from, to, _end);
				_end -= to - from;
				return from;
			}

			template<typename SUBST>
			iterator insert(iterator at, const SUBST& p)
			{
				return _construct_copy(_insert_raw(at), p);
			}

			template<typename SUBST>
			iterator insert(size_t at, const SUBST& p)
			{
				return insert(begin() + at, p);
			}

			iterator insert(iterator at)
			{
				return &_construct(_insert_raw(at));
			}

			iterator insert(size_t at)
			{
				return insert(begin() + at);
			}

			iterator insert_range(iterator at, size_t count)
			{
				iterator dst = _insert_raw(at, count);
				return _construct(dst, dst + count);
			}

			iterator insert_range(size_t at, size_t count)
			{
				return insert_range(begin() + at, count);
			}

			void move_items(iterator dst, iterator src_from, iterator src_to)
			{
				Log::memory::warning::assert(dst >= _begin && dst < _end && src_from < src_to && src_from >= _begin && src_to <= _end, T_("move iterators are out of master_vector"));
				if (dst > src_from)
				{
					size_t size = dst - src_from;
					dst = src_from;
					src_from = src_to;
					src_to = src_from + size;
					Log::memory::warning::assert(src_to <= _end, T_("the sequence cannot be moved out of master_vector bounds"));
				}
				int temp_size = src_from - dst;
				iterator temp = _alloc(temp_size);
				_move(temp, dst, dst + temp_size);
				_move(dst, src_from, src_to);
				_move(src_to - temp_size, temp, temp + temp_size);
				_dispose_block(temp);
			}

			T& operator[] (size_t index)
			{
				Log::memory::warning::assert(index < size(), T_("vector[] bad index"));
				return _begin[index];
			}
			T& at(size_t index)
			{
				Log::memory::fatal::assert(index < size(), T_("vector.at() bad index"));
				return _begin[index];
			}
			const T& operator[] (size_t index) const
			{
				Log::memory::warning::assert(index < size(), T_("vector[] bad index"));
				return _begin[index];
			}
			const T& at(size_t index) const
			{
				Log::memory::fatal::assert(index < size(), T_("vector.at() bad index"));
				return _begin[index];
			}
		protected:
			iterator _insert_raw(iterator at, size_t count = 1)
			{
				Log::memory::warning::assert(at >= _begin && at <= _end, T_("insert index out of vector"));
				if (_end + count >= _last_alloc)
				{
					size_t alloc_size = _grow_strategy.to_alloc(size(), count);
					iterator dst = _alloc(alloc_size);
					iterator dst_at = dst + (at - _begin);
					_last_alloc = dst + alloc_size;
					_move(dst, _begin, at);
					_move(dst_at + count, at, _end);
					_end = dst + size() + count;
					_dispose_block(_begin);
					_begin = dst;
					return dst_at;
				}
				_r_move(_end, _end - count, at);
				_end += count;
				return at;
			}

			void _realloc(size_t new_size)
			{
				iterator dst = _alloc(new_size);
				_last_alloc = dst + new_size;
				_move(dst, _begin, _end);
				_end = dst + size();
				_dispose_block(_begin);
				_begin = dst;
			}

			iterator _alloc(size_t to_allocate)
			{
				return to_allocate ? 
					reinterpret_cast<T*>(new char [to_allocate * sizeof(T)]) :
				NULL;
			}

			static void _dispose_block(iterator block)
			{
				delete[] reinterpret_cast<char*>(block);
			}
			void _construct(iterator from, iterator to)
			{
				while (from < to)
					_construct(from++);
			}
			void _destruct(iterator from, iterator to)
			{
				while (from < to)
					_destruct(from++);
			}
			void _construct_copy(iterator dst, iterator src_from, iterator src_to)
			{
				while (src_from < src_to)
					_construct_copy(dst++, *src_from++);
			}
			void _move(iterator dst, iterator src_from, iterator src_to)
			{
				::ak::ltm::in_move = true;
				_construct_copy(dst, src_from, src_to);
				::ak::ltm::in_move = false;
			}
			void _r_move(iterator dst, iterator src_from, iterator src_to)
			{
				::ak::ltm::in_move = true;
				while (src_from >= src_to)
					_construct_copy(dst--, *src_from--);
				::ak::ltm::in_move = false;
			}
			static T& _construct(void* ptr)
			{
				return *new(ptr) T;
			}
			template<typename SRC>
			static T* _construct_copy(void* dst, const SRC& src)
			{
				return new(dst) T(src);
			}
			static void _destruct(void* ptr)
			{
				reinterpret_cast<T*>(ptr)->~T();
			}
			T* _begin;
			T* _end;
			T* _last_alloc;

			grow_strategy _grow_strategy;
		};
		template<typename T, typename grow_strategy>
		inline void unsafe_swap(vector<T, grow_strategy>& a, vector<T, grow_strategy>& b)
		{
			std::swap(a._begin, b._begin);
			std::swap(a._end, b._end);
			std::swap(a._last_alloc, b._last_alloc);
			std::swap(a._grow_strategy, b._grow_strategy);
		}

		template<typename A, typename V>
		inline typename A::iterator index_of(A& arr, const V& val, typename A::iterator i)
		{
			for (; i != arr.end(); ++i)
			{
				if (*i == val)
					break;
			}
			return i;
		}
	}
}

#endif // _AK_LTM_VECTOR_H_
